#include "CNativeHandlerDummy.h"
#include "CNativeHandlerPrivate.h"

#include "CApplication.h"

class DummyNotifyFilter : public CAppNotifyFilter {
public:
    DummyNotifyFilter(CNativeHandlerDummy *handler) : CAppNotifyFilter(handler){};
    bool notifyFilter(QObject *obj, QEvent *event) override {
        return qobject_cast<CNativeHandlerDummy *>(this->obj)->notifyFilter(obj, event);
    };
};

#ifdef Q_OS_DUMMY
CNativeHandlerDummy::CNativeHandlerDummy(QWidget *parent) : CNativeHandler(parent) {
    hideWindowFrame();

    m_mousePressed = false;
    m_mousePoint = QPoint(0, 0);
    m_mouseRect = QRect(0, 0, 0, 0);

    m_resizeEnable = true;
    m_moveEnable = true;

    m_pressedArea.fill(false, 8);
    m_pressedRect.fill(QRect(0, 0, 0, 0), 8);

    w->setAttribute(Qt::WA_Hover);

    // Capture global mouse events
    qApp->installNotifyFilter(new DummyNotifyFilter(this));
}

CNativeHandlerDummy::~CNativeHandlerDummy() {
    qApp->removeNotifyFilter(this);
}

void CNativeHandlerDummy::showEventEx(QShowEvent *event) {
    Q_UNUSED(event)
    centralize();
    return CNativeHandler::showEventEx(event);
}

void CNativeHandlerDummy::windowStateChangeEventEx(QWindowStateChangeEvent *event) {
    if (event->type() == QEvent::WindowStateChange) {
        if (w->windowState() == Qt::WindowNoState) {
            m_moveEnable = true;
            m_resizeEnable = true;
        } else {
            m_moveEnable = false;
            m_resizeEnable = false;
        }
    }
}

bool CNativeHandlerDummy::commonEventFilter(QEvent *event) {
    if (dummyEventFilter(event)) {
        return true;
    }
    return CNativeHandler::commonEventFilter(event);
}

bool CNativeHandlerDummy::notifyFilter(QObject *obj, QEvent *event) {
    if (obj != this) {
        switch (event->type()) {
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease: {
            if (obj->isWidgetType()) {
                QMouseEvent *e = static_cast<QMouseEvent *>(event);
                QWidget *widget = qobject_cast<QWidget *>(obj);
                QMouseEvent m = *e;
                m.setLocalPos(w->mapFromGlobal(widget->mapToGlobal(m.localPos().toPoint())));
                if (dummyEventFilter(&m)) {
                    return true;
                };
            }
        }
        default:
            break;
        }
    }
    return false;
}

bool CNativeHandlerDummy::dummyEventFilter(QEvent *event) {
    if (event->type() == QEvent::Resize) {
        // Re-calc anchor areas
        int width = w->width();
        int height = w->height();

        int left = m_resizeMargins.left();
        int right = m_resizeMargins.right();
        int top = m_resizeMargins.top();
        int bottom = m_resizeMargins.bottom();

        // Left anchor area
        m_pressedRect[0] = QRect(0, top, left, height - top - bottom);
        // Right anchor area
        m_pressedRect[1] = QRect(width - right, top, right, height - top - bottom);
        // Top anchor area
        m_pressedRect[2] = QRect(left, 0, width - left - right, top);
        // Bottom anchor area
        m_pressedRect[3] = QRect(left, height - bottom, width - left - right, bottom);

        // Top left anchor area
        m_pressedRect[4] = QRect(0, 0, left, top);
        // Top right anchor area
        m_pressedRect[5] = QRect(width - right, 0, right, top);
        // Bottom left anchor area
        m_pressedRect[6] = QRect(0, height - bottom, left, bottom);
        // Bottom right anchor area
        m_pressedRect[7] = QRect(width - right, height - bottom, right, bottom);
    } else if (event->type() == QEvent::HoverMove) {
        // Set cursor
        QHoverEvent *hoverEvent = (QHoverEvent *) event;
        QPoint point = hoverEvent->pos();
        if (m_resizeEnable && m_resizable) {
            if (m_pressedRect.at(0).contains(point)) {
                w->setCursor(Qt::SizeHorCursor);
            } else if (m_pressedRect.at(1).contains(point)) {
                w->setCursor(Qt::SizeHorCursor);
            } else if (m_pressedRect.at(2).contains(point)) {
                w->setCursor(Qt::SizeVerCursor);
            } else if (m_pressedRect.at(3).contains(point)) {
                w->setCursor(Qt::SizeVerCursor);
            } else if (m_pressedRect.at(4).contains(point)) {
                w->setCursor(Qt::SizeFDiagCursor);
            } else if (m_pressedRect.at(5).contains(point)) {
                w->setCursor(Qt::SizeBDiagCursor);
            } else if (m_pressedRect.at(6).contains(point)) {
                w->setCursor(Qt::SizeBDiagCursor);
            } else if (m_pressedRect.at(7).contains(point)) {
                w->setCursor(Qt::SizeFDiagCursor);
            } else {
                w->setCursor(Qt::ArrowCursor);
            }
        }

        // Calc the movement by mouse pos
        int offsetX = point.x() - m_mousePoint.x();
        int offsetY = point.y() - m_mousePoint.y();

        // Judge stretch or move
        if (m_moveEnable && m_mousePressed) {
            w->move(w->x() + offsetX, w->y() + offsetY);
        }

        // Normalize if mouse drag on caption when maximized
        if ((m_pressedArea[2] || m_mousePressed) && w->windowState() == Qt::WindowMaximized) {
            QRect normalRect = w->normalGeometry();
            float fx = (float) point.x() / (float) w->rect().width();
            int offsetY = point.y();

            w->setWindowState(Qt::WindowNoState);
            w->setGeometry(normalRect);

            int x1 = (int) (fx * (float) normalRect.width());
            x1 = point.x() - x1;
            int y1 = point.y() - offsetY;

            w->move(x1, y1);
            // Re-calc start pos and size
            m_mousePoint = w->mapFromGlobal(point);
            m_mouseRect = w->geometry();
            m_mousePressed = true;
            m_pressedArea[2] = false;
        }

        if (m_resizeEnable && m_resizable) {
            int rectX = m_mouseRect.x();
            int rectY = m_mouseRect.y();
            int rectW = m_mouseRect.width();
            int rectH = m_mouseRect.height();

            int minW = w->minimumWidth();
            int minH = w->minimumHeight();

            if (m_pressedArea.at(0)) {
                int resizeW = w->width() - offsetX;
                if (minW <= resizeW) {
                    w->setGeometry(w->x() + offsetX, rectY, resizeW, rectH);
                }
            } else if (m_pressedArea.at(1)) {
                w->setGeometry(rectX, rectY, rectW + offsetX, rectH);
            } else if (m_pressedArea.at(2)) {
                int resizeH = w->height() - offsetY;
                if (minH <= resizeH) {
                    w->setGeometry(rectX, w->y() + offsetY, rectW, resizeH);
                }
            } else if (m_pressedArea.at(3)) {
                w->setGeometry(rectX, rectY, rectW, rectH + offsetY);
            } else if (m_pressedArea.at(4)) {
                int resizeW = w->width() - offsetX;
                int resizeH = w->height() - offsetY;
                if (minW <= resizeW) {
                    w->setGeometry(w->x() + offsetX, w->y(), resizeW, resizeH);
                }
                if (minH <= resizeH) {
                    w->setGeometry(w->x(), w->y() + offsetY, resizeW, resizeH);
                }
            } else if (m_pressedArea.at(5)) {
                int resizeW = rectW + offsetX;
                int resizeH = w->height() - offsetY;
                if (minH <= resizeH) {
                    w->setGeometry(w->x(), w->y() + offsetY, resizeW, resizeH);
                }
            } else if (m_pressedArea.at(6)) {
                int resizeW = w->width() - offsetX;
                int resizeH = rectH + offsetY;
                if (minW <= resizeW) {
                    w->setGeometry(w->x() + offsetX, w->y(), resizeW, resizeH);
                }
                if (minH <= resizeH) {
                    w->setGeometry(w->x(), w->y(), resizeW, resizeH);
                }
            } else if (m_pressedArea.at(7)) {
                int resizeW = rectW + offsetX;
                int resizeH = rectH + offsetY;
                w->setGeometry(w->x(), w->y(), resizeW, resizeH);
            }
        }
    } else if (event->type() == QEvent::MouseButtonPress) {
        // Record mouse press coordinates
        QMouseEvent *mouseEvent = (QMouseEvent *) event;
        m_mousePoint = mouseEvent->pos();
        m_mouseRect = w->geometry();

        bool pressed = false;
        if (m_pressedRect.at(0).contains(m_mousePoint)) {
            m_pressedArea[0] = true;
            pressed = true;
        } else if (m_pressedRect.at(1).contains(m_mousePoint)) {
            m_pressedArea[1] = true;
            pressed = true;
        } else if (m_pressedRect.at(2).contains(m_mousePoint)) {
            m_pressedArea[2] = true;
            pressed = true;
        } else if (m_pressedRect.at(3).contains(m_mousePoint)) {
            m_pressedArea[3] = true;
            pressed = true;
        } else if (m_pressedRect.at(4).contains(m_mousePoint)) {
            m_pressedArea[4] = true;
            pressed = true;
        } else if (m_pressedRect.at(5).contains(m_mousePoint)) {
            m_pressedArea[5] = true;
            pressed = true;
        } else if (m_pressedRect.at(6).contains(m_mousePoint)) {
            m_pressedArea[6] = true;
            pressed = true;
        } else if (m_pressedRect.at(7).contains(m_mousePoint)) {
            m_pressedArea[7] = true;
            pressed = true;
        } else {
            Q_D_MOUSE_AVAILABLE
            if (d) {
                m_mousePressed = true;
                pressed = true;
            }
        }
        if (pressed) {
            return true;
        }
    } else if (event->type() == QEvent::MouseMove) {
        // Proxy by hover move
    } else if (event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::Leave) {
        // Recover all
        w->setCursor(Qt::ArrowCursor);
        bool pressed = m_mousePressed;
        m_mousePressed = false;
        for (int i = 0; i < 8; ++i) {
            pressed = pressed || m_pressedArea.at(i);
            m_pressedArea[i] = false;
        }
        if (pressed) {
            return true;
        }
    } else if (event->type() == QEvent::MouseButtonDblClick) {
        Q_D_MOUSE_AVAILABLE
        if (m_resizable && d) {
            if (w->windowState() == Qt::WindowMaximized) {
                normalize();
                return true;
            } else {
                maximize();
                return true;
            }
        }
    } else if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) {
        if (m_resizable) {
            normalize();
            return true;
        }
    }
    return false;
}
#endif
